package minijava.Frame.Mips;

import minijava.Util.BoolList;
import minijava.Temp.*;
import minijava.Frame.*;
import java.util.HashMap;

/*
 * Fonte: http://www.cesarkallas.net/arquivos/faculdade/arquitetura-computadores/teoria/cap3hp.pdf
 * 32 Registradores de 32 bits($0..$31)
 * Convencao para registradores MIPS -
 *   $zero - $0 - Constante 0
 *   $v0 - $2 - Resultado e avaliacoes de expressoes - Nao preservado
 *   $v1 - $3 - Resultado e avaliacoes de expressoes - Nao preservado
 *   $a0 - $4 - Argumento - Preservado
 *   $a1 - $5 - Argumento - Preservado
 *   $a2 - $6 - Argumento - Preservado
 *   $a3 - $7 - Argumento - Preservado
 *   $t0 - $8 - Temporario - Nao Preservado
 *   $t1 - $9 - Temporario - Nao Preservado
 *   $t2 - $10 - Temporario - Nao Preservado
 *   $t3 - $11 - Temporario - Nao Preservado
 *   $t4 - $12 - Temporario - Nao Preservado
 *   $t5 - $13 - Temporario - Nao Preservado
 *   $t6 - $14 - Temporario - Nao Preservado
 *   $t7 - $15 - Temporario - Nao Preservado
 *   $s0 - $16 - Salvos - Preservado
 *   $s1 - $17 - Salvos - Preservado
 *   $s2 - $18 - Salvos - Preservado
 *   $s3 - $19 - Salvos - Preservado
 *   $s4 - $20 - Salvos - Preservado
 *   $s5 - $21 - Salvos - Preservado
 *   $s6 - $22 - Salvos - Preservado
 *   $s7 - $23 - Salvos - Preservado
 *   $t8 - $24 - Temporario - Nao Preservado
 *   $t9 - $25 - Temporario - Nao Preservado
 *   $gp - $28 - Ponteiro Global - Preservado
 *   $sp - $29 - Stack Pointer - Preservado
 *   $fp - $30 - Frame Pointer - Preservado
 *   $ra - $31 - Endereco de retorno - Preservado
 */

public class MipsFrame extends minijava.Frame.Frame
{
    @Override
    public Frame newFrame(Label name, minijava.Util.BoolList arguments)
    {
        return new MipsFrame(name,arguments);
    }

    private static HashMap<Label,Integer> stackFrame = new HashMap<Label,Integer>();
    
    private MipsFrame(Label n, BoolList a)
    {
        /*
         * ADICIONANDO UMA NOVA FUNCAO NO FRAME
         * Para evitar o problema de saber qual funcao, qual frame, ja que uma funcao
         * pode ser chamada mais de uma vez e aparecer no frame mais de uma vez, entao
         * devemos saber quantas funcoes com o mesmo Label ja existe no frame, e entao,
         * adicionar um contador no Label da funcao chamada.
         */
        Integer functionCount = stackFrame.get(n);
        if(functionCount == null)
        {
            functionCount = new Integer(0);
            this.name = n;
        }
        else
        {
            functionCount = new Integer(functionCount.intValue()+1);
            String labelName = n.toString() + "." + functionCount;
            this.name = new Label(labelName);
        }

        stackFrame.put(this.name,functionCount);

        while (a != null)
        {
            formals = new AccessList(allocLocal(a.head),formals);
            a = a.tail;
        }
    }

    @Override
    public int wordSize()
    {
        return wordSize;
    }

    @Override
    public Access allocLocal(boolean escape)
    {
        //So inicializando mesmo o resultado com um valor que nao entra
        Access resultLocal = new InFrame(1);
        //Se escape = true -> Guardado localmente no Frame
        if( escape || argCount > 4 )
        {
            resultLocal = new InFrame(offset);
            offset += wordSize; //Definindo o proximo local da memoria
        }
        else
        {
            switch(argCount)
            {
                case 1: resultLocal = new InReg(A0); break;
                case 2: resultLocal = new InReg(A1); break;
                case 3: resultLocal = new InReg(A2); break;
                case 4: resultLocal = new InReg(A3); argCount = 1; break;
            }
            argCount++;
        }
        return resultLocal;
    }

    @Override
    public Temp framePointer()
    {
        return FP;
    }

    @Override
    public String tempMap(Temp temp)
    {
        return tempMap.get(temp);
    }

    //Retorna os registradores que devem ser salvos pelo chamador: T0-T9; V0; V1; A0-A3;
    public TempList callerSaves()
    {
        TempList t = new TempList(
        A0, new TempList(
        A1, new TempList(
        A2, new TempList(
        A3, new TempList(
        V0, new TempList(
        V1, new TempList(
        T0, new TempList(
        T1, new TempList(
        T2, new TempList(
        T3, new TempList(
        T4, new TempList(
        T5, new TempList(
        T6, new TempList(
        T7, new TempList(
        T8, new TempList(
        T9, null))))))))))))))));

        return t;

    }


    //Retorna os registradores que devem ser salcos pelo chamado: S0-S7; ZERO; AT; OSK0; OSK1; GP; SP; RA;
    public TempList calleeSaves()
    {
        TempList t = new TempList(
        ZERO, new TempList(
        OSK0, new TempList(
        OSK1, new TempList(
        RA, new TempList(
        AT, new TempList(
        GP, new TempList(
        SP, new TempList(
        S0, new TempList(
        S1, new TempList(
        S2, new TempList(
        S3, new TempList(
        S4, new TempList(
        S5, new TempList(
        S6, new TempList(
        S7, null)))))))))))))));

        return t;
    }

    //////Atributos//////

    private static final int wordSize = 4;
    private static int offset = 0; //Local da memoria
    //public static HashMap<Temp,Integer> memory = new HashMap<Temp,Integer>(); //Representa a memoria
    public int argCount = 1;//funciona como se fosse o k para ter controle sobre os registradores

    //////Registradores//////

    //Registradores: Comum para todos, portanto static e final
    //Seguindo a ordem dos numeros dos registradores
    static final Temp ZERO = new Temp();
    static final Temp AT = new Temp(); //Reservado para Assembler

    static final Temp V0 = new Temp(); //Resultado da funcao
    static final Temp V1 = new Temp(); //Resultado da segunda funcao

    static final Temp A0 = new Temp(); //argumento 1
    static final Temp A1 = new Temp(); //argumento 2
    static final Temp A2 = new Temp(); //argumento 3
    static final Temp A3 = new Temp(); //argumento 4

    static final Temp T0 = new Temp(); //Temporario
    static final Temp T1 = new Temp(); //Temporario
    static final Temp T2 = new Temp(); //Temporario
    static final Temp T3 = new Temp(); //Temporario
    static final Temp T4 = new Temp(); //Temporario
    static final Temp T5 = new Temp(); //Temporario
    static final Temp T6 = new Temp(); //Temporario
    static final Temp T7 = new Temp(); //Temporario

    static final Temp S0 = new Temp(); //Salvos
    static final Temp S1 = new Temp(); //Salvos
    static final Temp S2 = new Temp(); //Salvos
    static final Temp S3 = new Temp(); //Salvos
    static final Temp S4 = new Temp(); //Salvos
    static final Temp S5 = new Temp(); //Salvos
    static final Temp S6 = new Temp(); //Salvos
    static final Temp S7 = new Temp(); //Salvos

    static final Temp T8 = new Temp(); //Temporario
    static final Temp T9 = new Temp(); //Temporario

    static final Temp OSK0 = new Temp(); //Reservado para o kernel
    static final Temp OSK1 = new Temp(); //Reservado para o kernel

    static final Temp GP = new Temp(); //Ponteiro Global

    static final Temp SP = new Temp(); //Stack Pointer

    static final Temp FP = new Temp(); //Frame Pointer

    static final Temp RA = new Temp(); //Endereco de retorno

    private static final HashMap<Temp,String> tempMap = new HashMap<Temp,String>(32);
    static
    {
        tempMap.put(ZERO,"$0");
        tempMap.put(AT,"$1");
        tempMap.put(V0,"$2");
        tempMap.put(V1,"$3");
        tempMap.put(A0,"$4");
        tempMap.put(A1,"$5");
        tempMap.put(A2,"$6");
        tempMap.put(A3,"$7");
        tempMap.put(T0,"$8");
        tempMap.put(T1,"$9");
        tempMap.put(T2,"$10");
        tempMap.put(T3,"$11");
        tempMap.put(T4,"$12");
        tempMap.put(T5,"$13");
        tempMap.put(T6,"$14");
        tempMap.put(T7,"$15");
        tempMap.put(T8,"$24");
        tempMap.put(T9,"$25");
        tempMap.put(S0,"$16");
        tempMap.put(S1,"$17");
        tempMap.put(S2,"$18");
        tempMap.put(S3,"$19");
        tempMap.put(S4,"$20");
        tempMap.put(S5,"$21");
        tempMap.put(S6,"$22");
        tempMap.put(S7,"$23");
        tempMap.put(OSK0,"$26");
        tempMap.put(OSK1,"$27");
        tempMap.put(GP,"$28");
        tempMap.put(SP,"$29");
        tempMap.put(FP,"$30");
        tempMap.put(RA,"$31");
    }
}
